home *** CD-ROM | disk | FTP | other *** search
/ Computer Select (Limited Edition) / Computer Select.iso / dobbs / v16n12 / subclass.asc < prev    next >
Encoding:
Text File  |  1991-11-14  |  29.0 KB  |  844 lines

  1. _SUBCLASSING APPLICATIONS_
  2. by Mike Klein
  3.  
  4. [LISTING ONE]
  5.  
  6. # Standard Windows make file.  The utility MAKE.EXE compares the
  7. # creation date of the file to the left of the colon with the file(s)
  8. # to the right of the colon.  If the file(s) on the right are newer
  9. # then the file on the left, Make will execute all of the command lines
  10. # following this line that are indented by at least one tab or space.
  11. # Any valid MS-DOS command line may be used.
  12.  
  13. # This line allows NMAKE to work as well
  14. all: progedit.exe
  15.  
  16. # Update the resource if necessary
  17. progedit.res: progedit.rc progedit.h progedit.ico
  18.     rc -r progedit.rc
  19.  
  20. # Update the object file if necessary
  21. progedit.obj: progedit.c progedit.h
  22.     cl -W4 -c -AS -Gsw -Oad -Zp progedit.c
  23.  
  24. # Update the executable file if necessary, and if so, add the resource back in.
  25. progedit.exe: progedit.obj progedit.def
  26.     link /NOD progedit,,, libw slibcew, progedit.def
  27.     rc progedit.res
  28.  
  29. # If the .res file is new and the .exe file is not, update the resource.
  30. # Note that the .rc file can be updated without having to either 
  31. # compile or link the file.
  32. progedit.exe: progedit.res
  33.     rc progedit.res
  34.  
  35.  
  36.  
  37.  
  38. [LISITNG TWO]
  39.  
  40. #define IDC_TABAMT 100
  41.  
  42. int  PASCAL WinMain(HANDLE, HANDLE, LPSTR, int);
  43.  
  44. LONG FAR PASCAL MyMainWndProc(HWND, unsigned, WORD, LONG);
  45. BOOL FAR PASCAL TabAmount(HWND, unsigned, WORD, LONG);
  46.  
  47.  
  48.  
  49.  
  50. [LISTING THREE]
  51.  
  52. ; module-definition file for Progeddit -- used by LINK.EXE
  53.  
  54. NAME         PROGEDIT   ; application's module name
  55. DESCRIPTION  'Progedit - Programming Editor'
  56. EXETYPE      WINDOWS       ; required for all Windows applications
  57. STUB         'WINSTUB.EXE' ; Generates error message if application
  58.                ; is run without Windows
  59.  
  60. ;CODE can be moved in memory and discarded/reloaded
  61. CODE  PRELOAD MOVEABLE DISCARDABLE
  62.  
  63. ;DATA must be MULTIPLE if program can be invoked more than once
  64. DATA  PRELOAD MOVEABLE MULTIPLE DISCARDABLE
  65.  
  66. HEAPSIZE     1024
  67. STACKSIZE    5120      ; recommended minimum for Windows applications
  68.  
  69. ; All functions that will be called by any Windows routine
  70. ; MUST be exported.
  71.  
  72. EXPORTS
  73.     MyMainWndProc @1
  74.     TabAmount     @2
  75.  
  76.  
  77.  
  78.  
  79. [LISTING FOUR]
  80.  
  81. #include <windows.h>
  82. #include "progedit.h"
  83.  
  84. ProgEdit ICON PROGEDIT.ICO
  85.  
  86. TabAmount DIALOG 11, 25, 75, 24
  87. CAPTION "Tab Amount"
  88. STYLE WS_POPUPWINDOW | WS_CAPTION
  89. BEGIN
  90.     CONTROL "Tab Amt:", -1, "static",
  91.         SS_RIGHT | WS_CHILD,
  92.         10, 6, 30, 12
  93.     CONTROL "4", IDC_TABAMT, "edit",
  94.         ES_LEFT | WS_BORDER | WS_TABSTOP | WS_CHILD,
  95.         45, 6, 20, 12
  96. END
  97.  
  98.  
  99.  
  100.  
  101.  
  102. [LISTING FIVE]
  103.  
  104. /*****************************************************************************
  105. PROGRAM: ProgEdit -- AUTHOR: Mike Klein -- VERSION: 1.0
  106. FILE: progedit.exe -- REQUIREMENTS: Windows 3.x 
  107. PURPOSE: Example of adding a menu item to a "foreign" application. In this 
  108. case, the program is Windows' NotePad, and the extension added is a definable 
  109. tab stop setting to Notepad's menu bar.
  110. *****************************************************************************/
  111.  
  112. #define _WINDOWS
  113. #define NOCOMM
  114.  
  115. #include <windows.h>
  116. #include <stdio.h>
  117. #include <string.h>
  118.  
  119. #include "progedit.h"
  120.  
  121. /* Handles & vars needed for ProgEdit */
  122. HANDLE  hInstProgEdit;
  123. FARPROC lpfnMyMainWndProc;
  124. /* Handles & vars needed for Notepad */
  125. HMENU   hMenuNotepad;
  126. HWND    hWndNotepadMain;
  127. HWND    hWndNotepadEdit;
  128. FARPROC lpfnNotepadMainWndProc;
  129.  
  130. int TabAmt;
  131.  
  132. BYTE Text[100];
  133.  
  134. /*****************************************************************************
  135.     FUNCTION: WinMain
  136.     PURPOSE : Calls initialization function, processes message loop
  137. *****************************************************************************/
  138. int PASCAL WinMain(HANDLE hInstance, HANDLE hPrevInstance, LPSTR lpCmdLine,
  139.     int nCmdShow)
  140. {
  141.     MSG msg;
  142.     struct
  143.     {
  144.         WORD wAlwaysTwo;
  145.         WORD wHowShown;
  146.     }
  147.     HowToShow;
  148.  
  149.     struct
  150.     {
  151.         WORD   wEnvSeg;
  152.         LPSTR  lpCmdLine;
  153.         LPVOID lpCmdShow;
  154.         DWORD  dwReserved;
  155.     }
  156.     ParameterBlock;
  157.     HowToShow.wAlwaysTwo    = 2;
  158.     HowToShow.wHowShown     = SW_SHOWNORMAL;
  159.     ParameterBlock.wEnvSeg    = 0;
  160.     ParameterBlock.lpCmdLine  = "";
  161.     ParameterBlock.lpCmdShow  = (LPVOID) &HowToShow;
  162.     ParameterBlock.dwReserved = NULL;
  163.     hInstProgEdit = hInstance;
  164.  
  165.     /* Run a copy of NotePad */
  166.     if(LoadModule("notepad.exe", (LPVOID) &ParameterBlock) < 32)
  167.     {
  168.         MessageBox(NULL, "Running instance of NotePad", "ERROR",
  169.             MB_OK | MB_ICONSTOP);
  170.         return(FALSE);
  171.     }
  172.  
  173.     /* Get handles to Notepad's two main windows */
  174.     hWndNotepadMain = GetActiveWindow();
  175.     hWndNotepadEdit = GetFocus();
  176.  
  177. /* Set up different function pointers. Get a ptr to my hWnd func, then 
  178. ** plug it into the other application's struct so it calls my func. Of 
  179. ** course, at end of my func, I call func that I stole in first place. */
  180.     lpfnMyMainWndProc=MakeProcInstance((FARPROC) MyMainWndProc, hInstProgEdit);
  181.     lpfnNotepadMainWndProc=(FARPROC) SetWindowLong(hWndNotepadMain,GWL_WNDPROC,
  182.                                                (DWORD) lpfnMyMainWndProc);
  183.  
  184.     /* Get handle to Notepad's menu and add Tabs to main menu */
  185.     hMenuNotepad = GetMenu(hWndNotepadMain);
  186.     AppendMenu(hMenuNotepad, MF_STRING, IDC_TABAMT, "&Tabs");
  187.     DrawMenuBar(hWndNotepadMain);
  188.  
  189.     /* Read in tab amt from win.ini */
  190.        GetProfileString("ProgEdit", "Tabs", "4", Text, 2);
  191.        TabAmt = (HIWORD(GetDialogBaseUnits()) * (Text[0] - '0')) / 4;
  192.        SendMessage(hWndNotepadEdit, EM_SETTABSTOPS, 1, (LONG) (LPINT) &TabAmt);
  193.  
  194.        /* Acquire and dispatch messages until a WM_QUIT message is received. */
  195.     while(GetMessage(&msg, NULL, NULL, NULL))
  196.     {
  197.         TranslateMessage(&msg);
  198.         DispatchMessage(&msg);
  199.     }
  200.     FreeProcInstance(lpfnMyMainWndProc);
  201.     return(FALSE);
  202. }
  203.  
  204. /*****************************************************************************
  205.     FUNCTION: MyMainWndProc
  206.     PURPOSE : Filter/replacement function for Notepad's MainWndProc()
  207. *****************************************************************************/
  208. LONG FAR PASCAL MyMainWndProc(HWND hWnd,unsigned wMsg,WORD wParam,LONG lParam)
  209. {
  210.     FARPROC lpProc;
  211.     switch(wMsg)
  212.     {
  213.         case WM_COMMAND :
  214.             switch(wParam)
  215.             {
  216.                 case IDC_TABAMT :
  217.                     /* Set tab stops in edit window */
  218.               lpProc = MakeProcInstance(TabAmount, hInstProgEdit);
  219.               DialogBox(hInstProgEdit, "TabAmount", hWnd, lpProc);
  220.               FreeProcInstance(lpProc);
  221.                 break;;
  222.             default :
  223.                 break;
  224.             }
  225.             break;
  226.         case WM_DESTROY :
  227.             SendMessage(hWndNotepadMain, WM_QUIT, 0, 0L);
  228.             PostQuitMessage(0);
  229.             break;
  230.         default :
  231.             break;
  232.     }
  233.    return(CallWindowProc(lpfnNotepadMainWndProc, hWnd, wMsg, wParam, lParam));
  234. }
  235.  
  236. /*****************************************************************************
  237.     FUNCTION: TabAmount
  238.     PURPOSE : Processes messages for edit window that gets tab amount
  239. *****************************************************************************/
  240. BOOL FAR PASCAL TabAmount(HWND hWnd, unsigned wMsg, WORD wParam, LONG lParam)
  241. {
  242.     switch(wMsg)
  243.     {
  244.         case WM_INITDIALOG :
  245.             /* Display the current tab setting */
  246.                      SendDlgItemMessage(hWnd, IDC_TABAMT, EM_LIMITTEXT, 1, 0L);
  247.              SetDlgItemInt(hWnd, IDC_TABAMT, (TabAmt * 4) /
  248.                 HIWORD(GetDialogBaseUnits()), FALSE);
  249.             return(TRUE);
  250.         case WM_COMMAND    :
  251.             switch(wParam)
  252.             {
  253.                 case IDOK         :
  254.                 case IDCANCEL     :
  255.  
  256.         /* Get number of tabs and calculate it in dialog units */
  257.         GetDlgItemText(hWnd, IDC_TABAMT, Text, sizeof(Text));
  258.          TabAmt = (HIWORD(GetDialogBaseUnits()) * (Text[0] - '0')) / 4;
  259.         /* Set the tab stops in the edit window */
  260.         SendMessage(hWndNotepadEdit, EM_SETTABSTOPS, 1,
  261.                                                       (LONG) (LPINT) &TabAmt);
  262.         InvalidateRect(hWndNotepadEdit, NULL, TRUE);
  263.         UpdateWindow(hWndNotepadEdit);
  264.         /* Save the tab amt in WIN.INI profile */
  265.         WriteProfileString("ProgEdit", "Tabs", Text);
  266.         EndDialog(hWnd, TRUE);
  267.                     return(TRUE);
  268.                 default          :
  269.                     break;
  270.             }
  271.             break;
  272.         default            :
  273.             break;
  274.     }
  275.     return(FALSE);
  276. }
  277.  
  278.  
  279.  
  280. [LISTING SIX]
  281.  
  282. # Standard Windows make file.  The utility MAKE.EXE compares the
  283. # creation date of the file to the left of the colon with the file(s)
  284. # to the right of the colon.  If the file(s) on the right are newer
  285. # then the file on the left, Make will execute all of the command lines
  286. # following this line that are indented by at least one tab or space.
  287. # Any valid MS-DOS command line may be used.
  288.  
  289. # This line allows NMAKE to work as well
  290. all: subclass.exe
  291.  
  292. # Update the resource if necessary
  293. subclass.res: subclass.rc subclass.h subclass.ico
  294.     rc -r subclass.rc
  295.  
  296. # Update the object file if necessary
  297. subclass.obj: subclass.c subclass.h
  298.     cl -W4 -c -AS -Gsw -Oad -Zip subclass.c
  299.  
  300. # Update the executable file if necessary, and if so, add the resource back in.
  301. subclass.exe: subclass.obj subclass.def
  302.     link /NOD /CO subclass,,, libw slibcew, subclass.def
  303.     rc subclass.res
  304.  
  305. # If the .res file is new and the .exe file is not, update the resource.
  306. # Note that the .rc file can be updated without having to either 
  307. # compile or link the file.
  308. subclass.exe: subclass.res
  309.     rc subclass.res
  310.  
  311.  
  312.  
  313.  
  314. [LISITNG SEVEN]
  315.  
  316.  
  317. /* Standard defines */
  318. #define FIRST       (0L)
  319. #define LAST        (0x7fff7fffL)
  320. #define ALL         (0x00007fffL)
  321.  
  322. #define IDC_LISTBOX     100
  323. #define IDC_INPUTBOX    100
  324.  
  325. /* Function prototypes */
  326. int  PASCAL WinMain(HANDLE, HANDLE, LPSTR, int);
  327.  
  328. LONG FAR PASCAL MainWndProc(HWND, unsigned, WORD, LONG);
  329. LONG FAR PASCAL HandleListBoxes(HWND, unsigned, WORD, LONG);
  330. LONG FAR PASCAL HandleEditCtrls(HWND, unsigned, WORD, LONG);
  331. VOID PASCAL CloseEditWindow(VOID);
  332. VOID PASCAL OpenEditWindow(DWORD);
  333.  
  334.  
  335.  
  336.  
  337. [LISTING EIGHT]
  338.  
  339. ; module-definition file for Megaphone -- used by LINK.EXE
  340.  
  341. NAME         Test   ; application's module name
  342. DESCRIPTION  'Test'
  343. EXETYPE      WINDOWS       ; required for all Windows applications
  344. STUB         'WINSTUB.EXE' ; Generates error message if application
  345.                ; is run without Windows
  346.  
  347. ;CODE can be moved in memory and discarded/reloaded
  348. CODE  PRELOAD MOVEABLE DISCARDABLE
  349.  
  350. ;DATA must be MULTIPLE if program can be invoked more than once
  351. DATA  PRELOAD MOVEABLE MULTIPLE
  352.  
  353. HEAPSIZE     1024
  354. STACKSIZE    5120      ; recommended minimum for Windows applications
  355.  
  356. ; All functions that will be called by any Windows routine
  357. ; MUST be exported.
  358.  
  359. EXPORTS
  360.     MainWndProc     @1
  361.     HandleListBoxes @2
  362.     HandleEditCtrls @3
  363.  
  364.  
  365.  
  366. [LISTING NINE]
  367.  
  368. /* Include files needed for .RC file */
  369. #include "windows.h"
  370. #include "subclass.h"
  371.  
  372. /* The program's icon (not that it needs one) */
  373. SubClass   ICON SUBCLASS.ICO
  374.  
  375. /* Main dialog w/listbox used by SubClass */
  376. SubClass DIALOG 36, 34, 100, 100
  377. CAPTION "SubClass"
  378. CLASS   "SubClass"
  379. STYLE   WS_POPUPWINDOW | WS_CAPTION | WS_MINIMIZEBOX | DS_LOCALEDIT
  380. BEGIN
  381.     CONTROL "", IDC_LISTBOX, "BetterListBox",
  382.         LBS_HASSTRINGS | LBS_NOTIFY | LBS_NOINTEGRALHEIGHT |
  383.         WS_BORDER | WS_VSCROLL | WS_CHILD,
  384.         10, 10, 80, 80
  385. END
  386.  
  387.  
  388. [LISTING TEN]
  389.  
  390. /*****************************************************************************
  391.     PROGRAM: SubClass -- AUTHOR: Mike Klein -- VERSION: 1.0
  392.     FILE : subclass.exe -- REQUIREMENTS: Windows 3.x
  393.     PURPOSE: An example of a subclassed listbox, providing enhanced input 
  394.     and data-entry facilities.
  395. *****************************************************************************/
  396.  
  397. /* Some std defines needed */
  398. #define _WINDOWS
  399. #define NOCOMM
  400.  
  401. /* INCLUDE files */
  402. #include <windows.h>
  403. #include "subclass.h"
  404.  
  405. /* Global variables */
  406. HANDLE  hInstSubClass;
  407. HWND    hDlgSubClass;
  408. HWND    hWndListBox;
  409. HWND    hWndEdit;
  410.  
  411. int     CurrentIndex;
  412. int     NumListBoxItems;
  413. RECT    CurrentItemRect;
  414.  
  415. BOOL    InsideEditMode = FALSE;
  416. DWORD   dwEditPos;
  417.  
  418. BYTE    InputString[50];
  419.  
  420. /* Far pointers to Windows' class functions for listboxes and edit ctrls */
  421. FARPROC lpfnListBox;
  422. FARPROC lpfnEditCtrl;
  423.  
  424. /*****************************************************************************
  425.     FUNCTION: WinMain
  426.     PURPOSE : Calls initialization function, processes message loop
  427. *****************************************************************************/
  428. int PASCAL WinMain(HANDLE hInstance, HANDLE hPrevInstance, LPSTR lpCmdLine,
  429.     int nCmdShow)
  430. {
  431.     WNDCLASS wc;
  432.     MSG msg;
  433.     if(!hPrevInstance)
  434.     {
  435.         hInstSubClass = hInstance;
  436.            /* Fill in window class structure with parameters that describe the
  437.        ** main window */
  438.         wc.style         = CS_DBLCLKS;
  439.         wc.lpfnWndProc   = MainWndProc;
  440.         wc.cbClsExtra    = 0;
  441.         wc.cbWndExtra    = DLGWINDOWEXTRA;
  442.         wc.hInstance     = hInstSubClass;
  443.         wc.hIcon         = LoadIcon(hInstSubClass, "SubClass");
  444.         wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
  445.         wc.hbrBackground = GetStockObject(WHITE_BRUSH);
  446.         wc.lpszMenuName  = NULL;
  447.         wc.lpszClassName = "SubClass";
  448.         if(!RegisterClass(&wc))
  449.             return(FALSE);
  450.            /* Fill in window class structure with parameters that describe our
  451.        ** custom list box -- BetterListBox */
  452.         wc.style         = CS_DBLCLKS;
  453.         wc.lpfnWndProc   = HandleListBoxes;
  454.         wc.cbClsExtra    = 0;
  455.         wc.cbWndExtra    = 0;
  456.         wc.hInstance     = hInstSubClass;
  457.         wc.hIcon         = NULL;
  458.         wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
  459.         wc.hbrBackground = GetStockObject(WHITE_BRUSH); 
  460.         wc.lpszMenuName  = NULL;
  461.         wc.lpszClassName = "BetterListBox";
  462.         if(!RegisterClass(&wc))
  463.             return(FALSE);
  464.         /* Fill in window class structure with parameters that describe our
  465.         ** custom edit control -- BetterEditCtrl */
  466.         wc.style         = CS_DBLCLKS;
  467.         wc.lpfnWndProc   = HandleEditCtrls;
  468.         wc.cbClsExtra    = 0;
  469.         wc.cbWndExtra    = 0;
  470.         wc.hInstance     = hInstSubClass;
  471.         wc.hIcon         = NULL;
  472.         wc.hCursor       = LoadCursor(NULL, IDC_IBEAM);
  473.         wc.hbrBackground = GetStockObject(WHITE_BRUSH); 
  474.         wc.lpszMenuName  = NULL;
  475.         wc.lpszClassName = "BetterEditCtrl";
  476.         if(!RegisterClass(&wc))
  477.             return(FALSE);
  478.               /* Get information on listbox class, so we can find out what its
  479.           ** class window function is. */ 
  480.         if(GetClassInfo(NULL, "listbox", &wc) == FALSE)
  481.             return(FALSE);
  482.         else
  483.             lpfnListBox = (FARPROC) wc.lpfnWndProc;
  484.       /* Get information on edit control class, so we can find out what its
  485.       ** class window function is. */
  486.         if(GetClassInfo(NULL, "edit", &wc) == FALSE)
  487.             return(FALSE);
  488.         else
  489.             lpfnEditCtrl = (FARPROC) wc.lpfnWndProc;
  490.  
  491.         /* Create the main window */
  492.         if((hDlgSubClass = CreateDialog(hInstSubClass, "SubClass",
  493.             NULL, 0L)) == NULL)
  494.         {
  495.             return(FALSE);
  496.         }
  497.         /* Get an oft used handle */
  498.         hWndListBox = GetDlgItem(hDlgSubClass, IDC_LISTBOX);
  499.         /* Put in some test strings. */
  500.         SendMessage(hWndListBox, LB_ADDSTRING, 0, (LONG) (LPSTR) "computer");
  501.     SendMessage(hWndListBox, LB_ADDSTRING, 0, (LONG) (LPSTR) "telephone");
  502.     SendMessage(hWndListBox, LB_ADDSTRING, 0, (LONG) (LPSTR) "lcd");
  503.     SendMessage(hWndListBox, LB_ADDSTRING, 0, (LONG) (LPSTR) "ochessica");
  504.     SendMessage(hWndListBox, LB_ADDSTRING, 0, (LONG) (LPSTR) "heeyah");
  505.     SendMessage(hWndListBox, LB_ADDSTRING, 0, (LONG) (LPSTR) "video");
  506.     SendMessage(hWndListBox, LB_ADDSTRING, 0, (LONG) (LPSTR) "smoke");
  507.     SendMessage(hWndListBox, LB_ADDSTRING, 0, (LONG) (LPSTR) "sky");
  508.     SendMessage(hWndListBox, LB_ADDSTRING, 0, (LONG) (LPSTR) "lovely");
  509.     SendMessage(hWndListBox, LB_ADDSTRING, 0, (LONG) (LPSTR) "windows");
  510.     SendMessage(hWndListBox, LB_ADDSTRING, 0, (LONG) (LPSTR) "nunez");
  511.     SendMessage(hWndListBox, LB_ADDSTRING, 0, (LONG) (LPSTR) "beer");
  512.     SendMessage(hWndListBox, LB_ADDSTRING, 0, (LONG) (LPSTR) "pug");
  513.     SendMessage(hWndListBox, LB_ADDSTRING, 0, (LONG) (LPSTR) "query");
  514.     SendMessage(hWndListBox, LB_ADDSTRING, 0, (LONG) (LPSTR) "remote");
  515.     SendMessage(hWndListBox, LB_ADDSTRING, 0, (LONG) (LPSTR) "party");
  516.     SendMessage(hWndListBox, LB_ADDSTRING, 0, (LONG) (LPSTR) "mixer");
  517.     SendMessage(hWndListBox, LB_ADDSTRING, 0, (LONG) (LPSTR) "skate");
  518.     SendMessage(hWndListBox, LB_ADDSTRING, 0, (LONG) (LPSTR) "varied");
  519.     SendMessage(hWndListBox, LB_ADDSTRING, 0, (LONG) (LPSTR) "interests");
  520.         /* Give the listbox an initial selection */
  521.         SendMessage(hWndListBox, LB_SETCURSEL, 0, 0L);
  522.         ShowWindow(hDlgSubClass, nCmdShow);
  523.         UpdateWindow(hDlgSubClass);
  524.     }
  525.     else
  526.     {
  527.     /* If there was another instance of SubClass running, then switch
  528.     ** to it by finding any window of class = "SubClass". Then, if it's
  529.     ** an icon, open the window, otherwise just make it active. */
  530.         hDlgSubClass = FindWindow("SubClass", NULL);
  531.         if(IsIconic(hDlgSubClass))
  532.             ShowWindow(hDlgSubClass, SW_SHOWNORMAL);
  533.         SetActiveWindow(hDlgSubClass);
  534.         return(FALSE);
  535.     }
  536.        /* Acquire and dispatch messages until a WM_QUIT message is received. */
  537.     while(GetMessage(&msg, NULL, NULL, NULL))
  538.     {
  539.         TranslateMessage(&msg);
  540.         DispatchMessage(&msg);
  541.     }
  542. }
  543.  
  544. /*****************************************************************************
  545.     FUNCTION: MainWndProc
  546.     PURPOSE : Processes messages for SubClass dialog box
  547. *****************************************************************************/
  548. LONG FAR PASCAL MainWndProc(HWND hWnd, unsigned wMsg, WORD wParam,
  549.     LONG lParam)
  550. {
  551.     switch(wMsg)
  552.     {
  553.         case WM_CLOSE :
  554.             DestroyWindow(hDlgSubClass);
  555.             return(0L);
  556.         case WM_SETFOCUS :
  557.             SetFocus(hWndListBox);
  558.             return(0L);
  559.         case WM_DESTROY :
  560.             PostQuitMessage(0);
  561.             return(0L);
  562.         default :
  563.             break;
  564.     }
  565.     return(DefDlgProc(hWnd, wMsg, wParam, lParam));
  566. }
  567.  
  568. /*****************************************************************************
  569.     FUNCTION: OpenEditWindow
  570.     PURPOSE : Opens edit window inside listbox
  571. *****************************************************************************/
  572. VOID PASCAL OpenEditWindow(DWORD CharSel)
  573. {
  574.     /* Flag telling us were in edit mode */
  575.     InsideEditMode = TRUE;
  576.     /* Find out current index into listbox */
  577.     CurrentIndex = (int) SendMessage(hWndListBox, LB_GETCURSEL, 0, 0L);
  578.     if(CurrentIndex == LB_ERR)
  579.         CurrentIndex = 0;
  580.     /* Find out what the text is in selected listbox cell */
  581.     SendMessage
  582.     (
  583.         hWndListBox,
  584.         LB_GETTEXT,
  585.         CurrentIndex,
  586.         (LONG) (LPSTR) InputString
  587.     );
  588.     /* Get client dimensions of listbox cell with respect to the entire
  589.     ** listbox and create an edit window right inside of it. */
  590.     SendMessage
  591.     (
  592.         hWndListBox,
  593.         LB_GETITEMRECT,
  594.         CurrentIndex,
  595.         (DWORD) (LPRECT) &CurrentItemRect
  596.     );
  597.     hWndEdit = CreateWindow
  598.     (
  599.         "BetterEditCtrl",
  600.         "",
  601.         ES_AUTOHSCROLL | ES_LEFT | WS_VISIBLE | WS_CHILD,
  602.         CurrentItemRect.left + 2,
  603.         CurrentItemRect.top,
  604.         CurrentItemRect.right - CurrentItemRect.left - 2,
  605.         CurrentItemRect.bottom - CurrentItemRect.top,
  606.         hWndListBox,
  607.         IDC_INPUTBOX,
  608.         hInstSubClass,
  609.         0L
  610.     );
  611.     /* Pre-fill the edit control with what was in the listbox cell */
  612.     SetWindowText(hWndEdit, InputString);
  613.     SetFocus(hWndEdit);
  614.     SendMessage(hWndEdit, EM_SETSEL, 0, CharSel);
  615. }
  616.  
  617. /*****************************************************************************
  618.     FUNCTION: CloseEditWindow
  619.     PURPOSE : Closes edit window inside listbox
  620. *****************************************************************************/
  621. VOID PASCAL CloseEditWindow(VOID)
  622. {
  623.     /* Flag telling us were aren't in edit mode anymore */
  624.     InsideEditMode = FALSE;
  625.     /* Get text of what was entered into edit control */
  626.     GetWindowText(hWndEdit, InputString, sizeof(InputString));
  627.     if(!GetWindowTextLength(hWndEdit))
  628.     {
  629.         DestroyWindow(hWndEdit);
  630.         SendMessage(hWndListBox, WM_KEYDOWN, VK_DELETE, 0L);
  631.         return;
  632.     }
  633.     /* Turn redrawing for the listbox off. */
  634.     SendMessage(hWndListBox, WM_SETREDRAW, 0, 0L);
  635.     /* Find out the RECT of the currently selected listbox item */
  636.     SendMessage(hWndListBox, LB_GETITEMRECT, CurrentIndex, (DWORD) (LPRECT)
  637.         &CurrentItemRect);
  638.     /* Delete the old string and add the new one */
  639.     SendMessage(hWndListBox, LB_INSERTSTRING, CurrentIndex,
  640.         (LONG) (LPSTR) InputString);
  641.     SendMessage(hWndListBox, LB_DELETESTRING, CurrentIndex + 1, 0L);
  642.     /* Destroy the old edit window. */
  643.     DestroyWindow(hWndEdit);
  644.       /* Validate the whole listbox and then invalidate only the list box rect
  645.     ** that we put the edit window into. */
  646.     ValidateRect(hWndListBox, NULL);
  647.     InvalidateRect(hWndListBox, &CurrentItemRect, TRUE);
  648.  
  649.     /* Turn re-drawing for the listbox back on and send a WM_PAINT for the 
  650.     ** entry changes to take effect. */
  651.     SendMessage(hWndListBox, WM_SETREDRAW, 1, 0L);
  652.     UpdateWindow(hWndListBox);
  653.     SetFocus(hWndListBox);
  654. }
  655.  
  656. /*****************************************************************************
  657.     FUNCTION: HandleListBoxes
  658.     PURPOSE : Process keystrokes and mouse for list boxes
  659. *****************************************************************************/
  660. LONG FAR PASCAL HandleListBoxes(HWND hWnd, unsigned wMsg, WORD wParam,
  661.     LONG lParam)
  662. {
  663.     switch(wMsg)
  664.     {
  665.         case WM_LBUTTONDBLCLK :
  666.  
  667.             /* Go into edit mode and put caret at end of edit ctrl. */
  668.             if(SendMessage(hWnd, LB_GETCOUNT, 0, 0L))
  669.             {
  670.                 OpenEditWindow(LAST);
  671.             }
  672.             return(0L);
  673.         case WM_LBUTTONDOWN :
  674.             if(InsideEditMode)
  675.             {
  676.             /* Find out cursor pos from the edit ctrl, so we can
  677.             ** use same positioning for cell were moving into */
  678.             dwEditPos = SendMessage(hWndEdit, EM_GETSEL, 0, 0L);
  679.                 CloseEditWindow();
  680.             /* Tell listbox to move cur ptr up or down based on
  681.             ** the mouse position, and open a new edit window */
  682.                 SendMessage(hWndListBox, wMsg, wParam, lParam);
  683.                 OpenEditWindow
  684.                 (
  685.                 MAKELONG(LOWORD(dwEditPos), LOWORD(dwEditPos))
  686.                 );
  687.                 return(0L);
  688.             }
  689.             break;
  690.         case WM_MBUTTONDBLCLK :
  691.         case WM_RBUTTONDBLCLK :
  692.         /* Make middle & right mouse buttons like left mouse button. */
  693.             SendMessage(hWnd, WM_LBUTTONDBLCLK, wParam, lParam);
  694.             break;
  695.         case WM_MBUTTONDOWN :
  696.         case WM_RBUTTONDOWN :
  697.         /* Make middle & right mouse buttons like left mouse button. */
  698.             SendMessage(hWnd, WM_LBUTTONDOWN, wParam, lParam);
  699.             SendMessage(hWnd, WM_LBUTTONUP, wParam, lParam);
  700.             break;
  701.         case WM_KEYDOWN :
  702.             switch(wParam)
  703.             {
  704.                 case VK_RETURN :
  705.             /* Enter was pressed, so go into edit mode and put the
  706.             ** caret at the end of the edit ctrl */
  707.                 if(SendMessage(hWnd, LB_GETCOUNT, 0, 0L))
  708.                     {
  709.                         OpenEditWindow(LAST);
  710.                     }
  711.                     return(0L);
  712.                 case VK_INSERT :
  713.             /* The INS key (add a new string). First, get currently 
  714.             ** selected entry. If none exists, assume that focus is on 
  715.             ** the first cell */
  716.             CurrentIndex = 
  717.                      (int) SendMessage(hWnd, LB_GETCURSEL, 0, 0L);
  718.                     if(CurrentIndex == LB_ERR)
  719.                     {
  720.                         CurrentIndex = 0;
  721.                     }
  722.                /* Find out what the text is in selected listbox cell */
  723.                         if(SendMessage(hWnd, LB_GETCOUNT, 0, 0L))
  724.                     {
  725.                         SendMessage
  726.                         (
  727.                             hWnd,
  728.                             LB_GETTEXT,
  729.                             CurrentIndex,
  730.                             (LONG) (LPSTR) InputString
  731.                         );
  732.                     }
  733.                     else
  734.                     {
  735.                 /* If nothing's in the listbox, then copy a null to
  736.                 ** the edit control. */
  737.                 InputString[0] = '\0';
  738.                     }
  739.  
  740.                     /* Insert new entry */
  741.                     SendMessage
  742.                     (
  743.                         hWnd,
  744.                         LB_INSERTSTRING,
  745.                         CurrentIndex,
  746.                         (LONG) (LPSTR) InputString
  747.                     );
  748.                /* Let our "edit current cell" function take over */
  749.                     OpenEditWindow(ALL);
  750.                     return(0L);
  751.                 case VK_DELETE :
  752.               /* The DEL key. If no items are in the listbox, then
  753.               ** return. Else, get the currently selected item. */
  754.                 if(!(NumListBoxItems = (int)
  755.                       SendMessage(hWnd, LB_GETCOUNT, 0, 0L)))
  756.                     {
  757.                         break;
  758.                     }
  759.                     if((CurrentIndex = (int)
  760.                 SendMessage(hWnd, LB_GETCURSEL, 0, 0L)) == LB_ERR)
  761.                     {
  762.                         CurrentIndex = 0;
  763.                     }
  764.             /* Delete the string. Tried to get rid of annoying
  765.             ** focus rect; couldn't. Too many inconsitencies in
  766.             ** the way Windows handles list and combo boxes. */
  767.             SendMessage(hWnd, LB_DELETESTRING, CurrentIndex, 0L);
  768.                     if(CurrentIndex == NumListBoxItems - 1)
  769.                     {
  770.                         --CurrentIndex;
  771.                     }
  772.                     /* Reset our listbox selection. */
  773.                  SendMessage(hWnd, LB_SETCURSEL, CurrentIndex, 0L);
  774.                     return(0L);
  775.                 default :
  776.                     break;
  777.             }
  778.             break;
  779.         default :
  780.             break;
  781.     }
  782.    /* Return any unprocessed messages to window's original class procedure. */
  783.     return(CallWindowProc(lpfnListBox, hWnd, wMsg, wParam, lParam));
  784. }
  785.  
  786. /*****************************************************************************
  787.     FUNCTION: HandleEditCtrls
  788.     PURPOSE : Process keystrokes and mouse for edit controls
  789. *****************************************************************************/
  790. LONG FAR PASCAL HandleEditCtrls(HWND hWnd, unsigned wMsg, WORD wParam,
  791.     LONG lParam)
  792. {
  793.     switch(wMsg)
  794.     {
  795.         case WM_LBUTTONDBLCLK :
  796.              /* Turn of edit mode, closing the edit window */
  797.             CloseEditWindow();
  798.             return(0L);
  799.         case WM_KEYDOWN :
  800.             switch(wParam)
  801.             {
  802.                 case VK_RETURN :
  803.                    /* Turn of edit mode, closing the edit window */
  804.                     CloseEditWindow();
  805.                     return(0L);
  806.                 case VK_DELETE :
  807.               /* Delete a character if one exists in the edit ctrl.
  808.               ** Otherwise, if the cell is blank, delete the entire
  809.               ** cell. */
  810.                 if(!GetWindowTextLength(hWnd))
  811.                 {
  812.                 CloseEditWindow();
  813.                 SendMessage(hWndListBox, wMsg, wParam, lParam);
  814.                 return(0L);
  815.                 }
  816.                 break;
  817.                 case VK_DOWN    :
  818.                 case VK_UP      :
  819.                 case VK_PRIOR   :
  820.                 case VK_NEXT    :
  821.               /* Find out cursor pos from edit ctrl, so we can
  822.               ** use same positioning for cell we're moving into */
  823.               dwEditPos = SendMessage(hWndEdit, EM_GETSEL, 0, 0L);
  824.                     CloseEditWindow();
  825.                 /* Tell listbox to move cur ptr up or down, and
  826.                 ** open an edit window at the new pos. */
  827.                 SendMessage(hWndListBox, wMsg, wParam, lParam);
  828.                 OpenEditWindow
  829.                     (
  830.                  MAKELONG(LOWORD(dwEditPos), LOWORD(dwEditPos))
  831.                     );
  832.                     return(0L);
  833.                 default :
  834.                     break;
  835.             }
  836.             break;
  837.         default :
  838.             break;
  839.     }
  840.    /* Return any unprocessed messages to window's original class procedure. */
  841.     return(CallWindowProc(lpfnEditCtrl, hWnd, wMsg, wParam, lParam));
  842. }
  843.  
  844.